/**
 *
 * $Id: MaCICtrlClient.i,v 1.2 2009-05-05 17:04:30 ttaipalu Exp $
 *
 * \brief Python bindings for following by Tapio Taipalus <tapio.taipalus@tkk.fi>
 * \file MaCICtrlClient.hpp
 * \brief MaCICtrl Interface Client header
 * \author Antti Maula <antti.maula@tkk.fi>
 */

%include std_vector.i
%include std_string.i

%{
#include "MaCIError.hpp"
#include "MaCICtrlData.hpp"
#include "gimiutils.h"
#include "sync.hpp"
#include <vector>
#include "MaCICtrlClient.hpp"
%}

// Forward declare gimi::GIMI
namespace gimi {
  class GIMI;
  class GIMIMessage;
}

namespace MaCI {
  namespace MaCICtrl {

    /** Typedef for Vector of TAccess elements.
     */
    typedef std::vector<TAccess> TAccessArray;


    /** Structure representing a MaCISL clause in separate fields.
     *
     */
    struct SMaCISL {
      /** Empty constructor.
       */
      SMaCISL(void)
        : group(),
          interface(),
          instance() {}
      
      /** Constructor to define all parts manually.
       * 
       * @param[in] aGroup      MaCIGroup to use.
       * @param[in] aInterface  Interface name to use (NOTE: this must match GIMI registered Interface name
       * @param[in] aInstance   Name of instance.
       */
      SMaCISL(const std::string &aGroup,
              const std::string &aInterface,
              const std::string &aInstance)
        : group(aGroup),
          interface(aInterface),
          instance(aInstance) {}
      
      /** Constructor to define all parts.
       *
       * @param[in] aGroup      MaCIGroup to use.
       * @param[in] aInterface  Interface number to use. GIMI is used to convert it to correct name string.
       * @param[in] aInstance   Name of instance.
       */
      SMaCISL(const std::string &aGroup,
              const unsigned int &aInterface,
              const std::string &aInstance)
        : group(aGroup),
          interface(),
          instance(aInstance) {
        SetInterface(aInterface);
      }
      

      /** Constructor to split a MaCISL expression string to parts.
       */
      SMaCISL(const std::string &aMaCISL)
        : group(),
          interface(),
          instance() {
        FromMaCISL(aMaCISL);
      }

      /** Print SMaCISL contents as MaCISL expression.
       *
       * @param[in] l           dPrint level to use for printing.
       */
      void Print(const int l) const {
        if (l <= debugGetPrintLevel()) {
          dPrint(l,"MaCISL: '%s'", ToMaCISLString().c_str());
        }
      }
      
      /** Split a MaCISL expression to fields.
       *
       * @param aMaCISL         MaCI Service Locator string to split.
       * @return                'true' if succesfully parsed, otherwise 'false'.
       */
      bool FromMaCISL(const std::string aMaCISL) {
        bool result = false;

        // Find LAST TWO dots. 
        const size_t dot_last_index = aMaCISL.rfind(".", std::string::npos);
        const size_t dot_2ndlast_index = aMaCISL.rfind(".", dot_last_index-1);

        // Check positions - need atleast the two dots.
        if (dot_last_index != std::string::npos && 
            dot_2ndlast_index != std::string::npos) {

          group.clear();
          interface.clear();
          instance.clear();

          instance.append(aMaCISL, dot_last_index+1, std::string::npos);
          interface.append(aMaCISL, dot_2ndlast_index+1, (dot_last_index - dot_2ndlast_index)-1);
          group.append(aMaCISL, 0, dot_2ndlast_index);
          
          // If wildcard was given, clear the field.
          if (group == std::string("*")) group.clear();
          if (interface == std::string("*")) interface.clear();
          if (instance == std::string("*")) instance.clear();
          result = true;
        }
        return result;
      }
      
      /** Construct a MaCISL expression.
       *
       * \deprecated This function is deprecated, as its name is
       * inconsistent with the type naming scheme. Use the
       * ToMaCISLString function instead.
       *
       * @return                MaCISL expression.
       *                        
       */
/*      std::string ToMaCISL(void) const __attribute__((deprecated))
      {
        return ToMaCISLString();
      }
*/

      /** Construct a MaCISL expression and return as single string.
       *
       * If any field is UNSET (empty), it will be replaced with '*',
       * forming an wildcard expresion.
       *
       * @return                MaCISL expression as single string.
       */
      std::string ToMaCISLString(void) const {
        const std::string result = 
          (group.size()     ? group     : "*") + "." + 
          (interface.size() ? interface : "*") + "." + 
          (instance.size()  ? instance  : "*");
        return result;
      }

      /** Set MaCI Group.
       * @param[in] aGroup      Group to set.
       */
      void SetGroup(const std::string &aGroup) {
        group = aGroup;
      }

      /** Set Instance name.
       * @param[in] aInstance   Instance name to set.
       */
      void SetInstance(const std::string &aInstance) {
        instance = aInstance;
      }

      /** Set Interface name. 
       *
       * This function sets the interface name.
       * \note This name should be consistent with names returned by
       * GIMI datatypeDefinitions engine, as it is used to identify
       * interfaces automatically.
       * @param[in] aInterface  Interface name to set.
       */
      void SetInterface(const std::string &aInterface) {
        interface = aInterface;
      }
      
      /** Set Interface name by service.
       *
       * This function requests the interface name from GIMI by using
       * the prodided service major number. If the type is not
       * registered with GIMI, 'Unknown' will be stored instead.
       * @param[in] aInterfaceNumber GIMI Major number to translate 
       *                             and store.
       */
      void SetInterface(const unsigned int aInterfaceNumber) {
        if (gimi::datatypeDefinitions.getDatatypeName(interface,
                                                      aInterfaceNumber) == false) {
          interface = "Unknown";
        }
      }
      
      /** Does this MaCISL instance contain a valid fully qualified
       * MaCISL expression?
       *
       * This function returns boolean value describing whether this
       * SMaCISL instance contains a fully qualified and valid MaCISL
       * expression. Expression is valid when it doesn't contain
       * invalid characters, and fully qualified when all three parts
       * are defined.
       *
       * \todo Validity is not really checked yet.
       *
       * @return                'true' when instance is valid & FQ,
       *                        otherwise 'false'.
       */
      bool IsValidFQMaCISL(void) const {
        return 
          group.size() && 
          interface.size() &&
          instance.size();
      }
      

      std::string group;        ///< Group part of MaCISL
      std::string interface;    ///< Interface part of MaCISL
      std::string instance;     ///< Instance part of MaCISL
    };

    /** Structure containing essential information concerning one MaCI
     * service.
     */
    struct SServiceEntry {
      SServiceEntry()
        : gimnetname(),
          gimnetid(),
          devicegroup(),
          service(),
          identification() {}
      

      /** Print entry contents through dPrint facility.
       */
      void Print(unsigned int l) const {
        dPrint(l,"################## Entry Begin ###########################");
        const std::string macislstr = GetMaCISL().ToMaCISLString();
        dPrint(l,
               "SServiceEntry (%p) contents:\n"
               "\tname: '%s' (%u bytes)\n"
               "\tgimnetid: 0x%08x\n"
               "\tMaCISL: '%s' (%u bytes)",
               this,
               gimnetname.c_str(), gimnetname.size(),
               gimnetid,
               macislstr.c_str(), macislstr.size());
        service.Print(l);
        identification.Print(l);
        dPrint(l,"################## Entry End #############################");
      }


      /** Returns the MaCI Service Locator (MaCISL) for this service.
       */
      SMaCISL GetMaCISL(void) const {
        return SMaCISL(devicegroup, 
                       service.servicemajor, 
                       (identification.interfaceinstancename[0] ? 
                        identification.interfaceinstancename : "Unknown"));
      }
      

      /** Returns GIMI service name for the assigned service major.
       *
       * @return GIMI servicename for the currently assigned
       * 'service.servicemajor', or 'Unknown' if service name is not
       * known by GIMI.
       */
      const std::string GetInterfaceName(void) const {
        std::string result("Unknown");
      
        // Unconditional call.
        gimi::datatypeDefinitions.getDatatypeName(result,
                                                  service.servicemajor);

        // Return the result.
        return result;
      }


      std::string gimnetname; ///< GIMnet name of client containing this service.
      unsigned int gimnetid;  ///< GIMnet ID of client containing this service.
      std::string devicegroup; ///< Device group
      MaCI::MaCICtrl::TService service;       ///< MaCI Service informationx
      MaCI::MaCICtrl::TServiceIdentification identification; ///< MaCI Service Identification
    };

    /// Type for grouping multiple SServiceEntry elements in one container.
    typedef std::vector<MaCI::MaCICtrl::SServiceEntry> TServiceEntryArray;


    /** MaCI - MaCICtrl Client interface.
     * 
     * This class contains the methods and types for using a
     * MaCI::MaCICtrl service in the GIMnet.
     */
    class CMaCICtrlClient : private gim::CSync
    {
    public: // Instance specific members.
      /** Constructor.
       * 
       * Constructor of the MaCICtrlClient. This requires; a pointer
       * to Initialized and connected GIMI, minor number of this
       * interface (Used as GIMI minor number)
       *
       * @param[in] aGIMIPtr Pointer to GIMI used for communication
       * @param[out] aMinor Minor number of interface. If value
       *             is set to '-1', the minor will be autoprobed.
       */     
      CMaCICtrlClient(gimi::GIMI *aGIMIPtr, const int aMinor = -1);

      
      /** Destructor.
       */
      ~CMaCICtrlClient();
      

      /** Open client interface.
       */
      MaCI::EMaCIError Open(void);


      /** Close client interface.
       */
      MaCI::EMaCIError Close(void);


      /** Discover all MaCICtrl servers on the GIMnet.
       * 
       * This function launches GIMI Service discovery, and returns a
       * list of available MaCICtrl servers on the GIMnet.
       *
       * @param[out] aServiceList  Reference to GIMI servicelist to
       *                        be filled.
       * @param[in] aTimeout_ms Timeout for operation.
       * @return                EMaCIError type of return value.
       */
      MaCI::EMaCIError DiscoverMaCICtrlServices(gimi::t_serviceList &aServiceList,
                                          const unsigned int aTimeout_ms = 2000) const;


      /** Read MaCICtrl information from specified GIMnet node.
       *
       * This function queries the specified GIMnet node about its
       * MaCI configuration. All available MaCI Service information is
       * returned.
       *
       * @param[out] aServices   Reference to serviceentryarray
       *                         to store the information in. All received
       *                         valid replies are stored in this array.
       *                         The array is cleared before inserting anything.
       *                         the information will be copied to.
       * @param[in] aGIMnetName  GIMnet name to query data from
       * @param[in] aMinor       MaCICtrl instance to use on the specified node.
       *                         Default is zero.
       * @param[in] aTimeout_ms  Time to wait for operation to complete before
       *                         giving up. Value is specified in milliseconds
       *                         and defaults to 1000ms if not specified.
       * @return                 Value of type EMaCIError is returned. See
       *                         its description for details.
       */
      MaCI::EMaCIError GetServiceArray(MaCI::MaCICtrl::TServiceEntryArray &aServices,
                                 const std::string &aGIMnetName,
                                 const int aMinor = 0,
                                 const unsigned int aTimeout_ms = 1000) const;


      /** Read MaCICtrl information from specified GIMnet node.
       *
       * This function queries the specified GIMnet node about its
       * MaCI configuration. All available MaCI Service information is
       * returned.
       *
       * \overload 
       *
       * @param[out] aServices   Reference to serviceentryarray
       *                         to store the information in. All received
       *                         valid replies are stored in this array.
       *                         The array is cleared before inserting anything.
       *                         the information will be copied to.
       * @param[in] aGIMnetID    GIMnet ID to query data from
       * @param[in] aMinor       MaCICtrl instance to use on the specified node.
       *                         Default is zero.
       * @param[in] aTimeout_ms  Time to wait for operation to complete before
       *                         giving up. Value is specified in milliseconds
       *                         and defaults to 1000ms if not specified.
       * @return                 Value of type EMaCIError is returned. See
       *                         its description for details.
       */
      MaCI::EMaCIError GetServiceArray(MaCI::MaCICtrl::TServiceEntryArray &aServices,
                                 const unsigned int &aGIMnetID,
                                 const int aMinor = 0,
                                 const unsigned int aTimeout_ms = 1000) const;
      

      /** Read MaCICtrl information from multiple targets.
       *
       * This function extracts information from multiple MaCICtrl
       * Servers. Using it is more efficient than calling the
       * name-based GetInformation() function in loop because the
       * requests are sent and processed in parallel.
       *
       * @param[in] aServiceList Servicelist to process. This is probably the
       *                         list just received from
       *                         DiscoverMaCICtrlServices() function.
       * @param[out] aServices   Reference to serviceentryarray
       *                         to store the information in. All received
       *                         valid replies are stored in this array.
       *                         The array is cleared before inserting anything.
       * @param[in] aTimeout_ms  Maximum time in milliseconds to wait for
       *                         the operation to complete. However, the operation
       *                         may complete as soon as all queried targets
       *                         have replied.
       * @return                 'KMaCIOK' on succesfull information Query,
       *                         other EMaCIError values on errors.
       */
      MaCI::EMaCIError GetServiceArray(MaCI::MaCICtrl::TServiceEntryArray &aServices,
                                 const gimi::t_serviceList &aServiceList,
                                 const unsigned int aTimeout_ms = 1000) const;
      


      /** Request authentication to service.
       *
       * This functions attempts to request authentication for the
       * specified MaCI module. Authentication scheme goes as follows:
       *
       * 1. The TAuthentication structure is sent to the module with
       * specified credentials.
       *
       * 2. The Module processes the query based on its current key
       * settings and sends back an TAccess reply which contains the
       * permissions for this client.
       *
       * @param[in] aGIMnetName   GIMnet name of node to attempt to authenticate on.
       * @param[in] aMajor        Major number of service to authenticate for.
       * @param[in] aMinor        Minor number of service to authenticate for.
       * @param[in] aAuthentication Authentication information entry.
       * @param[out] aAccess      Access information as returned by the
       *                          MaCICtrl service.
       * @param[in] aMaCICtrlMinor MaCICtrl server instance number (minor).
       *                          Defaults to zero.
       * @param[in] aTimeout_ms   Time to wait for operation to complete before
       *                          giving up. Value is specified in milliseconds
       *                          and defaults to 2000ms if not specified.
       * @return                  Value of type EMaCIError is returned. See
       *                          its description for details.
       */
      MaCI::EMaCIError RequestAuthentication(const std::string &aGIMnetName,
                                       const unsigned int aMajor, 
                                       const unsigned int aMinor,
                                       const MaCI::MaCICtrl::TAuthentication &aAuthentication,
                                       MaCI::MaCICtrl::TAccess &aAccess,
                                       const int aMaCICtrlMinor = 0,
                                       const unsigned int aTimeout_ms = 2000) const;
      

      
      /** Check for access errors on service.
       *
       * \todo This function is implemented as a STUB. However, the
       * documentation should be accurate.
       *
       * This function call checks whether this client has received an
       * access error notification from the server side of specified
       * service. Access error notification is generated when a client
       * attempts to execute a privileged operation without proper
       * authentication. Error is also generated when a previously got
       * authentication has timed out. Timed out sessions must be
       * revalidated in the same way as invalid or unvalidated
       * session.
       *
       * This function is designed to be a fast check, so it is
       * perfectly OK to call it each time a command is sent or like.
       * 
       * \see RequestAuthentication
       *
       *
       *       
       * @return                  Value of type EMaCIError is returned. See
       *                          its description for details.
       */
      MaCI::EMaCIError IsAccessError(const std::string &aGIMnetName,
                               const unsigned int aMajor, 
                               const unsigned int aMinor) 
      { return MaCI::KMaCIUnimplemented; }



    public: // Static members.

      /** Filter services by MaCI Service Locator.
       * 
       * This function filters the given list of services by given
       * MaCI Service Locator expression. The expression may contain
       * wildcards as elements, but currently not as parts (For
       * example 'Foo.*.Dummy' is valid, but 'Foo.Bar*.Dummy' is not)
       *
       * \note This function may return same SServiceEntry twice, as
       * Provided and Accepted type.
       *
       * @param[out] aDestServices Filtered entries are stored here.
       * @param[in] aSrcServices   Input array of services.
       * @param[in] aMaCISL        MaCISL expression to filter with.
       * @return                   'KMaCIOK' if the filter expression was
       *                           valid and was really used to filter 
       *                           input. Other EMaCIError values
       *                           as appropriate.
       */
      static MaCI::EMaCIError FilterServices(MaCI::MaCICtrl::TServiceEntryArray &aDestServices,
                                       const MaCI::MaCICtrl::TServiceEntryArray &aSrcServices,
                                       const MaCI::MaCICtrl::SMaCISL &aMaCISL);
      

      /** Finds a service by MaCI Service Locator.
       *
       * This function finds one single entry by filtering the given
       * TServiceEntryArray using the specified MaCISL
       * expression. This function will only succeed if the entry is
       * succesfully found, and is \b unique. If there is multiple
       * entries with identical MaCISL names in the GIMnet, this
       * function will fail (This usually causes other problems, so
       * failing here assists in finding these erroneus situations)
       * 
       * @param[out] aEntry        Single matching SServiceEntry.
       * @param[in] aSrcServices   Input array of services.
       * @param[in] aMaCISL        Service locator to search with.
       * @return                   'KMaCIOK' if the filter expression was
       *                           valid and one single entry was found.
       *                           Other EMaCIError values
       *                           as appropriate.
       */
      static MaCI::EMaCIError FindService(MaCI::MaCICtrl::SServiceEntry &aEntry,
                                    const MaCI::MaCICtrl::TServiceEntryArray &aSrcServices, 
                                    const MaCI::MaCICtrl::SMaCISL &aMaCISL);


      /** Find a MaCI Service Locator by GIMnetName/Major/Minor tuple.
       *
       * This function scans the given TServiceEntryArray attempting
       * to find a match for given parameters. If a match is found, it
       * is converted to SMaCISL format and stored in the given
       * reference.
       *
       * \note If you are using the ServiceType definition, please
       * note that MaCI and GIMI internally use different values. This
       * function accepts MaCI internal format, defined by
       * MaCICtrl::EServiceType.
       *
       * @param[out] aMaCISL    If entry is found, MaCISL expression is stored here.
       * @param[in] aSrcServices Input array of services.
       * @param[in] aGIMnetName GIMnetName to search for.
       * @param[in] aMajor      Major number to search for.
       * @param[in] aMinor      Minor number to search for.
       * @param[in] aServiceType MaCI EServiceType defining the service type.
       * @return                'KMaCIOK' if the filter expression was
       *                        valid and one single entry was found.
       *                        Other EMaCIError values
       *                        as appropriate.
       */
      static  MaCI::EMaCIError FindMaCISL(MaCI::MaCICtrl::SMaCISL &aMaCISL,
                                   const MaCI::MaCICtrl::TServiceEntryArray &aSrcServices,
                                   const std::string &aGIMnetName,
                                   const unsigned int aMajor,
                                   const unsigned int aMinor,
                                   const MaCI::MaCICtrl::EServiceType aServiceType = KServiceTypeAny);

    };

  }
}


